﻿using Microsoft.Practices.Prism.Logging ;
using Microsoft.Practices.Prism.Modularity ;
using Microsoft.Practices.ServiceLocation ;
using System ;
using System.IO.IsolatedStorage ;
using System.Linq ;
using System.Net ;
using OpenLightGroup.Common;

namespace OpenLightGroup.Prism
{

    public class WebModuleCatalog 
        : ModuleCatalog, ISynchronizingModuleCatalog
    {
     
        #region Ctor

        public WebModuleCatalog ( ILoggerFacade logger )
        : this( "../ModuleCatalogHandler.ashx", logger )
        {

        }

        public WebModuleCatalog ( string catalogRelativePath, ILoggerFacade logger )
        {
            this._logger = logger ;
            this.LoadCatalog( catalogRelativePath ) ;

        }

        public WebModuleCatalog ( System.Collections.Generic.IEnumerable<ModuleInfo> modules, string catalogRelativePath, ILoggerFacade logger )
        : base( modules )
        {
            this._logger = logger ;
            this.LoadCatalog( catalogRelativePath ) ;
        }

        #endregion

        #region Properties and Fields
  
        protected readonly string Setting_CatalogFilename = "ModuleCatalog.xaml" ;

        private ILoggerFacade _logger ;

        protected ILoggerFacade Logger
        {
            get
            {
                if ( this._logger == null )
                {
                    try
                    {
                        this._logger = ServiceLocator.Current.GetInstance<ILoggerFacade>( ) ;
                    }
                    catch (Exception)
                    {
                    }
                }

                return this._logger ;
            }
        }

        #endregion

        #region Web Methods

        protected virtual void LoadCatalog ( string catalogRelativePath )
        {
            if ( NetworkStatus.Current.IsOnline )
            {
                WebClient client = new WebClient( ) ;

                client.OpenReadCompleted += new OpenReadCompletedEventHandler( client_OpenReadCompleted ) ;

                Uri catalogUri = new Uri( WebSource.GetSourceDirectory( ), catalogRelativePath ) ;

                this.Logger.Log( "Downloading ModuleCatalog from " + catalogUri, Category.Debug, Priority.Low ) ;

                client.OpenReadAsync( catalogUri ) ;
            }
            else
            {
                this.GetCatalogFromLocalCache( ) ;
            }
        }

        protected virtual void client_OpenReadCompleted ( object sender, OpenReadCompletedEventArgs e )
        {
            if ( e.Error != null )
            {
                this.Logger.Log( "Failed to download module catalog from web.", Category.Exception, Priority.High ) ;

                this.Logger.Log( e.Error.Message, Category.Exception, Priority.High ) ;

                #if DEBUG
                this.Logger.Log( e.Error.StackTrace, Category.Debug, Priority.Low ) ;
                #endif

                this.GetCatalogFromLocalCache( ) ;

            }
            else if ( e.Cancelled )
            {
                this.GetCatalogFromLocalCache( ) ;
            }
            else
            {
                byte[] buffer = new byte[e.Result.Length] ;

                e.Result.Read( buffer, 0, Convert.ToInt32( e.Result.Length ) ) ;

                ModuleCatalog catalog ;

                using ( var mem = new System.IO.MemoryStream( buffer ) )
                    catalog = Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml( mem ) ;

                this.SyncCatalogs( catalog ) ;

                this.StoreCatalogInLocalCache( buffer ) ;

            }
        }

        #endregion

        #region Local Storage

        protected virtual void StoreCatalogInLocalCache ( byte[] buffer )
        {

            using ( var store = IsolatedStorageFile.GetUserStoreForApplication( ) )
                using ( var file = store.OpenFile( this.Setting_CatalogFilename, System.IO.FileMode.Create ) )
                    using ( var writer = new System.IO.StreamWriter( file ) )
                    {
                        writer.Write( System.Text.Encoding.UTF8.GetString( buffer, 0, buffer.Length ) ) ;

                        writer.Close( ) ;
                    }

        }

        protected virtual void GetCatalogFromLocalCache ( )
        {
            this.Logger.Log( "Attempting to get module catalog from local storage", Category.Debug, Priority.Low ) ;

            using ( var store = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication( ) )
            {
                
                if ( store.FileExists( this.Setting_CatalogFilename ) )
                {
                    using ( var file = store.OpenFile( this.Setting_CatalogFilename, System.IO.FileMode.Open ) )
                    {
                        var catalog = Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml( file ) ;

                        this.SyncCatalogs( catalog ) ;
                    }
                }
                else
                {
                    using ( var file = store.CreateFile( this.Setting_CatalogFilename ) )
                    {
                        file.Close( ) ;
                    }

                    this.Logger.Log( "Module catalog has not been cached locally.", Category.Warn, Priority.Medium ) ;
                }
            }
        }

        #endregion

        #region ISynchronizingModuleCatalog

        public event EventHandler CatalogSynchronized;

        public virtual void SyncCatalogs ( ModuleCatalog catalog )
        {
            if ( catalog != null )
            {
                this.Logger.Log( "Syncing module catalogs.", Category.Debug, Priority.Low ) ;

                foreach ( var item in catalog.Modules
                    .Where( m => this.Modules.Select( c => c.Ref ).Contains( m.Ref ) == false ) )

                    this.AddModule( item ) ;

                //ToDo Make sure this works!!!
                foreach ( var item in this.Modules.ToArray( )
                    .Where( m => catalog.Modules.Select( c => c.Ref ).Contains( m.Ref ) == false ) )

                    this.Items.Remove( item ) ;

                this.Initialize( ) ;

                //Notify a Synchronizing Module Manager to Run
                this.CatalogSynchronized.Raise( this ) ;

            }
            else
                this.Logger.Log( "Could not load module catalog.", Category.Warn, Priority.Medium ) ;
        }
     

        #endregion
    }
}